﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.Xml.Linq;
using ProtetoresDaMata.Data;
using ProtetoresDaMata.Objects;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
using System.Windows.Controls;
using Microsoft.Xna.Framework.Audio;
using ProtetoresDaMata.Physics;

namespace ProtetoresDaMata.Smarts
{
    public class Helpers
    {
        public Helpers()
        {
            LoadTotalScore();
        }

        XDocument XmlDoc = new XDocument();
        Score ScoreTotal;

        //Properties Base of Objects

        //Floor & Hole
        ScenarioObject floorBase;
        ScenarioObject holeBase;

        ScenarioObject montain_1;
        ScenarioObject montain_2;
        ScenarioObject montain_3;
        ScenarioObject montain_4;
        ScenarioObject montain_5;
        ScenarioObject montain_6;

        ScenarioObject sun;
        ScenarioObject moon;

        ScenarioObject cloud1;
        ScenarioObject cloud2;
        ScenarioObject cloud3;


        //Obstacles
        ScenarioObject elephantBase;
        ScenarioObject ostrichBase;

        //Bonus
        ScenarioObject orangeBase;

        //Sounds
        public Sounds sounds;


        int[] triangleSchemaPosition = new int[] { 200, 180, 150, 180, 200 };
        int[] lineSchemaPosition = new int[] { 280, 280, 280, 280, 280 };
        int[] lineUpSchemaPosition = new int[] { 150, 150, 150, 150, 150 };


        #region Sounds

        public void LoadSounds(ContentManager content)
        {
            sounds = new Sounds(content);
        }


        #endregion

        #region Rewards

        /// <summary>
        /// Methods that returns rewards by TotalScore
        /// </summary>
        /// <returns></returns>
        public List<RewardTO> GetRewards()
        {
            List<RewardTO> _rewards = new List<RewardTO>();

            XmlDoc = XDocument.Load("Xml/Rewards.xml");


            foreach (XElement item in XmlDoc.Elements("Rewards").Elements("Reward"))
            {
                if (Convert.ToInt32(item.Element("PointsNeeded").Value) < ScoreTotal.PointsScore)
                {
                    _rewards.Add(new RewardTO()
                    {
                        Name = (string)item.Element("Name").Value,
                        ImagePath = (string)item.Element("ImagePath").Value,
                        Description = (string)item.Element("Description").Value
                    });
                }
            }

            return _rewards;
        }

        #endregion

        #region Score

        /// <summary>
        /// Method that count and update the distance score
        /// </summary>
        /// <param name="sprite"></param>
        /// <param name="tb_distanceScore"></param>
        public void DistanceScoreUpdate(Character sprite, TextBlock tb_distanceScore)
        {
            tb_distanceScore.Text = Math.Ceiling((sprite.Position.X - 40) / 200).ToString();
        }

        /// <summary>
        /// Method that count and update the bonus score
        /// </summary>
        /// <param name="bonusValue"></param>
        /// <param name="tb_iconsScore"></param>
        public void IconsScoreUpdate(int bonusValue, TextBlock tb_iconsScore)
        {
            if (bonusValue != 0)
            {
                tb_iconsScore.Text = (Convert.ToInt32(tb_iconsScore.Text) + bonusValue).ToString();
            }

        }

        /// <summary>
        /// Method that load total score acumuled of all games
        /// </summary>
        private void LoadTotalScore()
        {
            using (GameDataContext context = new GameDataContext())
            {
                ScoreTotal = context.Scores.Where(obj => obj.PlayerName == "PointsAccumulated").First();
            }
        }

        /// <summary>
        /// Method that return list of 20 top high score
        /// </summary>
        /// <returns></returns>
        public List<Score> GetTop20Scores()
        {
            using (GameDataContext context = new GameDataContext())
            {
                return context.Scores.Where(item => item.PlayerName != "PointsAccumulated").Take(20).ToList();
            }
        }

        #endregion

        #region ScenarioObjects

        /// <summary>
        /// Method that load all objects scenario
        /// </summary>
        /// <param name="content"></param>
        public void LoadPropertiesScenarioObjects(ContentManager content)
        {

            XmlDoc = XDocument.Load("Xml/ScenarioObjects.xml");

            foreach (XElement item in XmlDoc.Elements("ScenarioObjects").Elements("ScenarioObject"))
            {

                switch (item.Element("Name").Value)
                {
                    case "Floor":
                        floorBase = new ScenarioObject()
                            {
                                Name = (string)item.Element("Name").Value,
                                Texture = content.Load<Texture2D>((string)item.Element("ImagePath").Value)
                            };
                        break;
                    case "Hole":
                        holeBase = new ScenarioObject()
                        {
                            Name = (string)item.Element("Name").Value,
                            Texture = content.Load<Texture2D>((string)item.Element("ImagePath").Value)
                        };
                        break;
                    case "Elephant":
                        elephantBase = new ScenarioObject()
                        {
                            Name = (string)item.Element("Name").Value,
                            Texture = content.Load<Texture2D>((string)item.Element("ImagePath").Value)
                        };
                        break;
                    case "Ostrich":
                        ostrichBase = new ScenarioObject()
                        {
                            Name = (string)item.Element("Name").Value,
                            Texture = content.Load<Texture2D>((string)item.Element("ImagePath").Value)
                        };
                        break;
                    case "Orange":
                        orangeBase = new ScenarioObject()
                        {
                            Name = (string)item.Element("Name").Value,
                            Texture = content.Load<Texture2D>((string)item.Element("ImagePath").Value),
                            Bonus = Convert.ToBoolean(item.Element("Bonus").Value),
                            BonusValue = Convert.ToInt32(item.Element("BonusValue").Value)
                        };
                        break;
                    case "Montain1":
                        montain_1 = new ScenarioObject()
                        {
                            Name = (string)item.Element("Name").Value,
                            Texture = content.Load<Texture2D>((string)item.Element("ImagePath").Value),
                        };

                        break;
                    case "Montain2":
                        montain_2 = new ScenarioObject()
                        {
                            Name = (string)item.Element("Name").Value,
                            Texture = content.Load<Texture2D>((string)item.Element("ImagePath").Value),
                        };

                        break;
                    case "Montain3":
                        montain_3 = new ScenarioObject()
                        {
                            Name = (string)item.Element("Name").Value,
                            Texture = content.Load<Texture2D>((string)item.Element("ImagePath").Value),
                        };

                        break;
                    case "Montain4":
                        montain_4 = new ScenarioObject()
                        {
                            Name = (string)item.Element("Name").Value,
                            Texture = content.Load<Texture2D>((string)item.Element("ImagePath").Value),
                        };

                        break;
                    case "Montain5":
                        montain_5 = new ScenarioObject()
                        {
                            Name = (string)item.Element("Name").Value,
                            Texture = content.Load<Texture2D>((string)item.Element("ImagePath").Value),
                        };

                        break;
                    case "Montain6":
                        montain_6 = new ScenarioObject()
                        {
                            Name = (string)item.Element("Name").Value,
                            Texture = content.Load<Texture2D>((string)item.Element("ImagePath").Value),
                        };
                        break;
                    case "Sun":
                        sun = new ScenarioObject()
                        {
                            Name = (string)item.Element("Name").Value,
                            Texture = content.Load<Texture2D>((string)item.Element("ImagePath").Value),
                        };
                        break;
                    case "Moon":
                        moon = new ScenarioObject()
                        {
                            Name = (string)item.Element("Name").Value,
                            Texture = content.Load<Texture2D>((string)item.Element("ImagePath").Value),
                        };

                        break;
                    default:
                        break;
                }


            }
        }

        /// <summary>
        /// Method that build initial floor
        /// </summary>
        /// <param name="floors"></param>
        public void BuildInitialFloor(ref List<ScenarioObject> floors)
        {
            for (int i = 0; i < 8; i++)
            {
                floors.Add(GetObjectBase(floorBase, new Vector2(i * 200, 400)));
            }

        }

        /// <summary>
        /// Method that build continued floors
        /// </summary>
        /// <param name="floors"></param>
        /// <param name="cameraX"></param>
        public void BuildContinuityFloor(ref List<ScenarioObject> floors, float cameraX)
        {
            if (cameraX % 200 == 0 && cameraX > 0)
            {

                if (RandomGenerator() == 0
                    && !floors.Last().Name.StartsWith("Hole"))
                {
                    //Insert "Hole" in list
                    floors.Add(GetObjectBase(holeBase, new Vector2(cameraX + 1425, 380)));
                }
                else
                {
                    //Insert "Floor" in list
                    floors.Add(GetObjectBase(floorBase, new Vector2(cameraX + 1400, 400)));
                }

                floors = floors.Where(obj => obj.Position.X > cameraX - 200).ToList();

            }

        }

        /// <summary>
        /// Build obstacles scheme
        /// </summary>
        /// <param name="floors"></param>
        /// <param name="obstacles"></param>
        /// <param name="cameraX"></param>
        public void BuildObstacles(List<ScenarioObject> floors, ref  List<ScenarioObject> obstacles, float cameraX)
        {
            var interator = RandomGenerator();
            obstacles = obstacles.OrderBy(x => x.Position.X).ToList();

            if (cameraX % 400 == 0 && cameraX > 0)
            {
                if (floors[3].Name == "Floor" &&
                    floors[4].Name == "Floor" &&
                    floors[5].Name == "Floor")
                {

                    switch (interator)
                    {
                        case 1:
                            obstacles.Add(GetObjectBase(elephantBase, new Vector2(cameraX + 850, 300)));
                            break;
                        case 2:
                            obstacles.Add(GetObjectBase(ostrichBase, new Vector2(cameraX + 860, 300)));
                            break;
                        case 3:
                            obstacles.Add(GetObjectBase(ostrichBase, new Vector2(cameraX + 860, 300)));
                            break;
                        default:
                            break;
                    }

                    if (obstacles.Count > 3)
                    {
                        obstacles.RemoveAt(0);
                    }
                }
            }

        }

        /// <summary>
        /// Build Bonus icons scheme
        /// </summary>
        /// <param name="floors"></param>
        /// <param name="obstacles"></param>
        /// <param name="bonus"></param>
        /// <param name="cameraX"></param>
        public void BuildBonusIcons(List<ScenarioObject> floors, List<ScenarioObject> obstacles, ref List<ScenarioObject> bonus, float cameraX)
        {
            var interator = RandomGenerator();
            //obstacles = obstacles.OrderBy(x => x.Position.X).ToList();

            float[] pointAB = new float[] { floors[4].Position.X, floors[4].Position.X + 200 };
            int[] schemePosition = GetSchemePosition(interator);


            if (cameraX % 200 == 0 && interator % 2 == 0)
            {
                if (floors[4].Name != "Hole" && !obstacles.Any(obj => obj.Position.X > pointAB[0] && obj.Position.X < pointAB[1]))
                {
                    for (int i = 0; i < 5; i++)
                    {
                        bonus.Add(GetObjectBase(orangeBase, new Vector2(cameraX + 800 + (i * 40), schemePosition[i])));
                    }
                }
                else
                {
                    for (int i = 0; i < 5; i++)
                    {
                        bonus.Add(GetObjectBase(orangeBase, new Vector2(cameraX + 800 + (i * 40), triangleSchemaPosition[i])));
                    }
                }
            }
        }

        /// <summary>
        /// Build initial background scene
        /// </summary>
        /// <param name="montains"></param>
        public void BuildInitialMontains(ref List<ScenarioObject> montains)
        {
            for (int i = 0; i < 4; i++)
            {
                switch (i)
                {
                    case 1:
                        montains.Add(GetObjectBase(montain_1, new Vector2(i * 400, 300)));
                        break;
                    case 2:
                        montains.Add(GetObjectBase(montain_2, new Vector2(i * 400, 300)));
                        break;
                    case 3:
                        montains.Add(GetObjectBase(montain_3, new Vector2(i * 400, 300)));
                        break;
                    case 4:
                        montains.Add(GetObjectBase(montain_4, new Vector2(i * 400, 300)));
                        break;
                    default:
                        break;
                }

            }
        }

        /// <summary>
        /// Build continuation of background scene
        /// </summary>
        /// <param name="montains"></param>
        /// <param name="cameraX"></param>
        public void BuildContinuosMontains(ref List<ScenarioObject> montains, float cameraX)
        {
            int interator = RandomGenerator();

            if (cameraX % 400 == 0)
                switch (interator)
                {
                    case 1:
                        montains.Add(GetObjectBase(montain_1, new Vector2(cameraX + 800, 300)));
                        break;
                    case 2:
                        montains.Add(GetObjectBase(montain_2, new Vector2(cameraX + 800, 300)));
                        break;
                    case 3:
                        montains.Add(GetObjectBase(montain_3, new Vector2(cameraX + 800, 300)));
                        break;
                    case 4:
                        montains.Add(GetObjectBase(montain_4, new Vector2(cameraX + 800, 300)));
                        break;
                    default:
                        break;
                }
        }

        /// <summary>
        /// Get bonus position scheme
        /// </summary>
        /// <param name="interator"></param>
        /// <returns></returns>
        private int[] GetSchemePosition(int interator)
        {
            if (interator == 1)
            {
                return triangleSchemaPosition;
            }
            else if (interator == 2)
            {
                return lineUpSchemaPosition;
            }
            else
            {
                return lineSchemaPosition;
            }
        }

        #endregion

        #region Collisions Verify

        /// <summary>
        /// Method that verify colision the character with obstacles
        /// </summary>
        /// <param name="list"></param>
        /// <param name="character"></param>
        /// <returns></returns>
        public bool ObstaclesCollisionsVerify(List<ScenarioObject> list, Character character)
        {
            if (list.Any(obj => obj.CollisionBounds.Intersects(character.CollisionBounds) && obj.Bonus == false))
                return false;

            return true;
        }

        /// <summary>
        /// Method that verify collision the collision with character
        /// </summary>
        /// <param name="list"></param>
        /// <param name="character"></param>
        public int BonusCollisionsVerify(ref List<ScenarioObject> list, Character character)
        {
            int bonusValue = 0;
            ScenarioObject objectRef = list.Where(obj => obj.CollisionBounds.Intersects(character.CollisionBounds)).FirstOrDefault();

            if (objectRef != null)
            {
                bonusValue = objectRef.BonusValue;
                list = list.Where(obj => !obj.CollisionBounds.Intersects(character.CollisionBounds)).ToList();

                //Sounds Fx
                sounds.BonusSmall.Play();
            }

            return bonusValue;
        }

        /// <summary>
        /// Method that verify return of collision and modify gamestate
        /// </summary>
        /// <param name="collisionHoles"></param>
        /// <param name="collisionObstacles"></param>
        /// <param name="GameState"></param>
        public void CollisionObstacles(bool collisionHoles, bool collisionObstacles, ref Enumerators.GameState GameState)
        {
            if (!collisionObstacles || !collisionHoles)
            {
                GameState = Enumerators.GameState.Over;
            }

        }

        #endregion

        #region Mooviments Methods

        /// <summary>
        /// 
        /// </summary>
        /// <param name="character"></param>
        public void CharacterMoviment(ref Character character)
        {
            character.Position.X += character.Velocity;
            character.CollisionBounds = this.GetRetangleBoundsCharacter(character);
            if (character.Position.X % 2 == 0)
            {
                //sounds.RunSteps.Play();
            }
        }

        public void DayNightTransition(float cameraX, ref bool dayNight)
        {
            if (cameraX % 5000 == 0 && cameraX > 0)
            {
                dayNight = !dayNight;
            }

        }

        public void DayNightChange(ref ScenarioObject sun, ref ScenarioObject moon, bool dayNight)
        {
            if (dayNight)
            {
                if (sun.Position.Y > 20)
                {
                    sun.Position.Y -= 10;
                    moon.Position.Y += 10;
                }

            }
            else
            {

                if (moon.Position.Y > 20)
                {
                    sun.Position.Y += 10;
                    moon.Position.Y -= 10;
                }
            }

            sun.Position.X += 8;
            moon.Position.X += 8;


        }

        public void LoadSunMoon(ref ScenarioObject sun, ref ScenarioObject moon)
        {
            sun = GetObjectBase(this.sun, new Vector2(50, 20));
            moon = GetObjectBase(this.moon, new Vector2(50, 500));
        }

        #endregion

        #region Methods Auxiliary


        /// <summary>
        /// Method that drawn objects in the screen
        /// </summary>
        /// <param name="spriteBatch"></param>
        /// <param name="objects"></param>
        public void DrawObjects(SpriteBatch spriteBatch, List<ScenarioObject>[] objects)
        {
            foreach (var item in objects)
            {
                foreach (var obj in item)
                {
                    obj.Draw(spriteBatch);
                }
            }
        }

        /// <summary>
        /// Methods that return a rectangle of collisions of the Scenario Objects
        /// </summary>
        /// <param name="sprite"></param>
        /// <returns></returns>
        private Rectangle GetRetangleBoundsGeneric(Sprite sprite)
        {
            if (sprite != null)
                return new Rectangle((int)sprite.Position.X ,
                                     (int)sprite.Position.Y,
                                     sprite.Texture.Width,
                                     sprite.Texture.Height);

            return new Rectangle();
        }

        /// <summary>
        /// Methods that return a rectangle of collisions of the character
        /// </summary>
        /// <param name="sprite"></param>
        /// <returns></returns>
        private Rectangle GetRetangleBoundsCharacter(Sprite sprite)
        {
            if (sprite != null)
                return new Rectangle((int)sprite.Position.X + 30,
                                     (int)sprite.Position.Y,
                                     sprite.Texture.Width - 100,
                                     sprite.Texture.Height);

            return new Rectangle();
        }


        /// <summary>
        /// Method that return a object  like your base
        /// </summary>
        /// <param name="objectBase"></param>
        /// <param name="position"></param>
        /// <returns></returns>
        private ScenarioObject GetObjectBase(ScenarioObject objectBase, Vector2 position)
        {
            var scenarioObject = new ScenarioObject()
            {
                Name = objectBase.Name,
                Texture = objectBase.Texture,
                Position = position,
                Bonus = objectBase.Bonus,
                BonusValue = objectBase.BonusValue,
            };

            scenarioObject.CollisionBounds = GetRetangleBoundsGeneric(scenarioObject);

            return scenarioObject;
        }

        /// <summary>
        /// Method that return random number for interactions
        /// </summary>
        /// <returns></returns>
        private int RandomGenerator()
        {
            return (int)(new Random().NextDouble() * 10);
        }



        #endregion
    }
}
